<?php

namespace WsdServer\CoreBase;

use Server\Components\Consul\ConsulServices;
use Server\CoreBase\ChildProxy;
use Server\CoreBase\Controller;
use Server\SwooleMarco;

use WsdServer\Exception\WsdException;
use WsdServer\Lib\Validation\Validator;
use WsdServer\Lib\Validation\ValidException;

/**
 * JsonRpcController 控制器
 * 对象池模式，实例会被反复使用，成员变量缓存数据记得在销毁时清理
 * Created by PhpStorm.
 * User: huangbo
 * Date: 2017-12-08
 * Time: 12:45
 */
class JsonRpcController extends Controller
{
    // Add by hb on 2018-01-03 for Support Doctrine-ORM
    protected $orm_conn;
    protected $orm_em;

    // Add by hb on 2018-04-04 for Support Laravel::Cache, Laravel::Database
    protected $laravel_cm;
    protected $laravel_db;

    protected $client_info;

    /**
     * JsonRpcController constructor.
     * @param string $proxy
     */
    public function __construct($proxy = ChildProxy::class)
    {
        parent::__construct($proxy);

        // Add by hb on 2018-01-03 for Support Doctrine-ORM
        $this->orm_conn = get_instance()->getDoctrineConn();
        $this->orm_em = get_instance()->getDoctrineEm();

        // Add by hb on 2018-04-04 for Support Laravel::Cache
        $this->laravel_cm = get_instance()->getLaravelCache();
        $this->laravel_db = get_instance()->getLaravelDatabase();
    }

    /**
     * 初始化每次执行方法之前都会执行initialization
     * @param string $controller_name 准备执行的controller名称
     * @param string $method_name 准备执行的method名称
     * @throws \Exception
     */
    protected function initialization($controller_name, $method_name)
    {
        parent::initialization($controller_name, $method_name);

        /*
        if ($this->amqp_pool != null) {
            $this->installAmqpPool($this->amqp_pool);
            $this->amqp_channel = $this->amqp_pool->channel;
        }
        */

        // Add by hb on 2018-01-31 for Reset Doctrine-ORM EntityManager
        if (!$this->orm_em->isOpen()) {
            $connection = $this->orm_em->getConnection();
            $config = $this->orm_em->getConfiguration();
            $this->orm_em = $this->orm_em->create($connection, $config);
        }

        $this->client_info = Array(
            "session_token" => $this->http_input->getRequestHeader('x-wlb-token'),             // 客户端请求头的原始Token
            "user_id" => $this->http_input->getRequestHeader('x-wsd-user-id'),               // 客户端当前登录的用户ID
            "ipaddr" => $this->http_input->getRequestHeader('x-wsd-user-ip'),                // 客户端IP地址
            "network" => $this->http_input->getRequestHeader('x-wsd-client-network'),        // 客户端网络连接类型：WIFI、3G、4G
            "client_type" => $this->http_input->getRequestHeader('x-wsd-client-type'),       // 客户端类型: pc, h5, android, ios, wechat
            "client_version" => $this->http_input->getRequestHeader('x-wsd-client-version'), // 客户端APP版本号
            "client_channel" => $this->http_input->getRequestHeader('x-wsd-client-channel'), // 客户端APP应用市场
            "device_desc" => $this->http_input->getRequestHeader('x-wsd-client-device'),     // 客户端终端设备厂商与型号
            "device_uuid" => $this->http_input->getRequestHeader('x-wsd-client-uuid'),       // 客户端终端设备厂商与型号
            "os_desc" => $this->http_input->getRequestHeader('x-wsd-client-os'),             // 客户端终端设备操作系统与版本信息
            "origin_agent" => $this->http_input->getRequestHeader('x-wsd-user-agent'),       // 原始User-Agent
            "origin_refer" => $this->http_input->getRequestHeader('x-wsd-referrer'),         // 原始引用页
        );

    }

    /**
     * 异常返回封装
     * @param $ex
     */
    protected function returnException($ex)
    {
        $err_code = $ex->getCode();
        $err_msg = $ex->getMessage();
        /*
        $err_detail = "";
        if ($ex instanceof ValidException){
            $err_detail = $ex->getErrDetail();
        }else if ($ex instanceof WsdException){
            $err_detail = $ex->getInternalMsg();
        }else{
            $err_msg = "系统错误";
            $err_detail = $ex->getMessage();
        }
        */

        $data['jsonrpc'] = 2.0;
        $data['error']['code'] = $err_code;
        $data['error']['message'] = $err_msg;
        //$data['error']['detail'] = $err_detail;
        $data['id'] = $this->rpc_request_id;

        //$data = array("id"=>$this->rpc_request_id, "jsonrpc"=>"2.0", "error"=>array(
        //                "code"=>$err_code, "message"=>$err_msg, "detail"=>$err_detail)
        //);
        $json_data = json_encode($data);
        $this->http_output->setHeader("Content-Type", "application/json");
        $this->http_output->end($json_data);
        return;
    }

    /**
     * 错误返回封装 - TODO： 兼容保留，有待整理
     * @param bool $code 错误码
     * @param bool $message 错误信息
     * @param int $id
     */
    protected function returnErrorAjax($code=false, $message=false, $id=1)
    {
        if(!$code) $code =400;
        if(!$message) $message='参数错误';
        $rArray['jsonrpc'] = 2.0;
        $rArray['error']['code'] = $code;
        $rArray['error']['message'] = $message;
        $rArray['id'] = $id;

        $data = array("id"=>$id, "jsonrpc"=>"2.0", "error"=>array(
            "code"=>$code, "message"=>$message)
        );
        $json_data = json_encode($data);
        $this->http_output->setHeader("Content-Type", "application/json");
        $this->http_output->end($json_data);
        //$this->http_output->end(json_encode($rArray));
        return;
    }

    /**
     * 正确返回封装
     * @param array $result
     * @param $id
     */
    protected function returnSuccessAjax($result=array(), $id=1)
    {
        $rArray['jsonrpc'] = 2.0 ;
        $rArray['id'] = $id ;
        $rArray['result']['code'] = 0;
        $rArray['result']['message'] = 'success' ;
        $rArray['result']['data'] = $result;
        $this->http_output->setHeader("Content-Type", "application/json");
        $this->http_output->end(json_encode($rArray));
    }

    /**
     * 当控制器方法不存在的时候的默认方法
     */
    public function defaultMethod()
    {
        if ($this->request_type == SwooleMarco::HTTP_REQUEST) {
            $this->returnErrorAjax('404','method is not find');
            //
        } else {
            throw new SwooleException($this->context['raw_method_name'] . ' method not exist');
        }
    }

    /**
     * 通过jsonrpc调用微服务的指定方法，并传入json参数
     * Add by hb on 2017-12-20
     * @param $serviceName : 用于寻找服务
     * @param $method
     * @param $params
     * @param $context
     * @param string $execServiceName ： 用于调用服务
     * @return mixed
     * @throws \Server\CoreBase\SwooleException
     */
    protected function sendJsonRpc($serviceName, $method, $params, $context, $execServiceName="") {
        $rest = ConsulServices::getInstance()->getRESTService($serviceName, $context);
        //echo "===== sendJsonRpc =====\n";
        $data = new \stdClass();
        $data->id = $context['request_id'];
        $data->jsonrpc = "2.0";
        $data->method = $method;
        $data->params = $params;
        $data_json = json_encode($data);
        //var_dump($data_json);

        $rest->addHeader("content-type", "application/json-rpc");
        $rest->setMethod("post");
        $rest->setData($data_json);

        //$resp = yield $rest->sendSms();
        if ($execServiceName==""){
            $execServiceName = $serviceName;
        }
        $resp = yield $rest->httpClient->coroutineExecute("/$execServiceName");
        $resp_body = json_decode($resp['body'], true);
        //var_dump($resp_body);

        return $resp_body;
    }

    /**
     * 发送MQ消息
     * Add by hb on 2018-01-18
     */
    protected function sendMqMsg($msg_body, $exchange, $routing_key){
        try {
            /*
            $amqp = new AMQP('192.168.20.238',5672,'guest','guest', '/');
            $channel = $amqp->channel();
            */
            $channel = get_instance()->getAsynPool('amqpPool')->getSync();
            /*
            $channel->queue_declare('TestConsumer', false, true, false, false);
            $channel->exchange_declare('TestConsumer', 'direct', false, true, false);
            $channel->queue_bind('TestConsumer', 'TestConsumer', 'TestConsumer');
            */
            $message = new AMQPMessage($msg_body, array('content_type' => 'application/json', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));
            $channel->basic_publish($message, $exchange, $routing_key);
            return true;
        } catch (\Exception $ex){
            secho("sendMqMsg-Exception", $ex.getMessage());
            return false;
        }
    }

    /**
     * 析构方法
     */
    public function destroy()
    {
        parent::destroy();

        // Add by hb on 2018-01-31 Support Doctrine-ORM : output debug message
        if (isset($this->orm_em)){
            $sqlLogger = $this->orm_em->getConfiguration()->getSQLLogger();
            if (isset($sqlLogger)){
                while (count($sqlLogger->queries)>0) {
                    $query= array_shift($sqlLogger->queries);
                    $sql = $query['sql'];
                    $sql_type = strtoupper(substr($sql, 0, 6));
                    //if ($sql_type=="INSERT" || $sql_type=="SELECT" || $sql_type=="DELETE"){
                        $params = self::formatParameters($query['params']);
                        $execMS = sprintf('%.4f',$query['executionMS']);
                        secho('ORM', "($execMS ms) || $sql || $params");
                    //}
                }
            }
        }
    }

    /**
     * Returns a human-readable representation of an array of parameters.
     * This properly handles binary data by returning a hex representation.
     *
     * @param array $params
     *
     * @return string
     */
    //private static function formatParameters(array $params)
    protected static function formatParameters($params)
    {
        if (!is_array($params)){
            return "";
        }

        return '[' . implode(', ', array_map(function ($param) {
                $json = @json_encode($param);

                if (! is_string($json) || $json == 'null' && is_string($param)) {
                    // JSON encoding failed, this is not a UTF-8 string.
                    return '"\x' . implode('\x', str_split(bin2hex($param), 2)) . '"';
                }

                return $json;
            }, $params)) . ']';
    }

    /**
     * 校验请求参数
     * @param $data
     * @param $rules
     * @return bool
     * @throws ValidException
     */
    protected function checkParams($data, $rules){
        $messages = [];
        $attributes = [];
        $validator = Validator::getInstance()->make($data, $rules, $messages, $attributes);
        //判断验证是否通过
        if ($validator->fails()) {
            throw new ValidException($validator->errors()->messages(), "1001");
        }
        return true;
    }
}
